/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.modules.java;
import java.io.*;
import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue;
import java.util.*;
import javax.swing.text.Document;
import javax.swing.text.StyledDocument;
import javax.swing.text.BadLocationException;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.text.NbDocument;
import org.openide.text.IndentEngine;
import org.openide.util.NbBundle;
import org.openide.src.*;
import org.openide.loaders.DataObject;
import org.openide.cookies.SaveCookie;
import org.netbeans.modules.java.settings.JavaSettings;
/** Miscellaneous utilities for Java data loader.
*
* @author Petr Hamernik, Ales Novak
*/
public final class Util extends Object {
// ===================== i18n utilities ===========================
/** ResourceBundle for java-loader package. */
static final ResourceBundle bundle = NbBundle.getBundle(Util.class);
/** Computes the localized string for the key.
* @param key The key of the string.
* @return the localized string.
*/
static String getString(String key) {
return bundle.getString(key);
}
// ===================== loader utilities ===========================
/** Finds file with the same name and specified extension
* in the same folder as param f
* @return the found fileobject or null.
*/
static FileObject findFile(FileObject f, String ext) {
if (f == null) {
return null;
}
String name = f.getName ();
int indx = name.indexOf ('$');
if (indx > 0) {
name = name.substring (0, indx);
}
FileObject ret = f.getParent().getFileObject (name, ext);
if (ret != null) {
return ret;
} else if (isPerfectRecognition() &&
JavaDataLoader.CLASS_EXTENSION.equals(f.getExt())) {
// try it from the current thread - see JavaCompiler - find createData string
if (f.getSize() == 0) {
String thrname = Thread.currentThread().getName();
int idx = thrname.indexOf('.');
if ((idx < 0) ||
(idx == thrname.length() - 1)
) {
return null;
}
if (! ext.equals(thrname.substring(idx + 1))) {
return null;
}
name = thrname.substring(0, idx);
return f.getParent().getFileObject(name, ext);
}
// examine class file
java.io.DataInputStream is = null;
try {
sun.tools.java.Environment env = JavaCompiler.createEnvironment(null);
is = new java.io.DataInputStream(new java.io.BufferedInputStream(f.getInputStream()));
sun.tools.java.ClassDefinition cdef = sun.tools.java.BinaryClass.load(env, is);
Object sorc = cdef.getSource();
if (sorc != null) {
String src = sorc.toString();
int idx = src.indexOf('.');
if ((idx <= 0) ||
(idx == src.length() - 1)
) {
return null;
}
name = src.substring(0, idx);
String xext = src.substring(idx + 1);
if (! xext.equals(ext)) {
return null;
}
return f.getParent().getFileObject(name, ext);
}
is.close();
} catch (java.io.IOException e) {
notifyException(e, f.toString());
} finally {
if (is != null) {
try {
is.close();
} catch (java.io.IOException e) {
notifyException(e, f.toString());
}
}
}
}
return null;
}
/** Notifies about an exception
*
* @param msg is ignored
*/
private static void notifyException(Throwable t, String msg) {
/*final org.openide.NotifyDescriptor e = new org.openide.NotifyDescriptor.Exception(t, msg);
Runnable run = new Runnable() {
public void run() {
org.openide.TopManager.getDefault().notify(e);
}
};
java.awt.EventQueue.invokeLater(run);
*/
org.openide.TopManager.getDefault().notifyException(t);
}
/**
* @return the value of the perfectRecognition property of JDO
*/
private static boolean isPerfectRecognition() {
JavaSettings jst = (JavaSettings) JavaSettings.findObject(JavaSettings.class, true);
return jst.isPerfectRecognition();
}
// ===================== Indentation util ==============================
/** Finds the appropriate indentation writer for the java sources.
* @param doc The document where it will be inserted in
* @param offset The position in the document
* @param writer The encapsulated writer
*/
static Writer findIndentWriter(Document doc, int offset, Writer writer) {
String mime = FileUtil.getMIMEType(JavaDataLoader.JAVA_EXTENSION);
IndentEngine engine = IndentEngine.find(mime == null ? "" : mime); // NOI18N
return engine.createWriter(doc, offset, writer);
}
// ===================== Advanced Task suppport =========================
/** Invokes the runnable using NbDocument.runAtomic.
* If BadLocationException occured inside, it will be thrown
* the new SourceException.
*
*/
static void runAtomic(StyledDocument doc, ExceptionRunnable run) throws SourceException {
RunnableSupport support = new RunnableSupport(run);
NbDocument.runAtomic(doc, support);
support.throwException();
}
/** This interface is used like runnable, but its method run
* could throw BadLocationException.
* @exception
*/
interface ExceptionRunnable {
public void run() throws Exception;
}
/** Encapsulation class for the ExceptionRunnable interface.
* It implements Runnable, so it can be used everywhere is Runnable
* accepted.
*/
private static class RunnableSupport implements Runnable {
/** Exception which occured in the e*/
private Exception e;
/** Encapsulated runnable */
private ExceptionRunnable runnable;
/** Creates new class */
public RunnableSupport(ExceptionRunnable runnable) {
this.runnable = runnable;
e = null;
}
/** @return true if exception occured. */
public boolean hasException() {
return e != null;
}
/** @return the exception or null if no exception has occured. */
public Exception getException() {
return e;
}
/** If a bad position exception occured during running
* the given ExceptionRunnable, it will be thrown new SourceException
* with the same message. Otherwise throws nothing.
*/
public void throwException() throws SourceException {
if (e != null) {
throw new SourceException(e.getMessage());
}
}
/** Implementation of the Runnable interface. It calls the encapsulated
* runnable passed in the constructor and catch the exceptions.
*/
public void run() {
try {
runnable.run();
}
catch (Exception e) {
this.e = e;
}
}
}
// ================== Compiler utilities ============================
/** WeakHashtable is used to avoid memory leak in the compiler. */
static class WeakHashtable extends Hashtable {
/** Reference queue for garbage collected values. */
private ReferenceQueue refq;
/** ref to original Hashtable */
private Hashtable hash;
static final long serialVersionUID =-6362145647944898749L;
/** @param hash is a Hashtable to to replace */
public WeakHashtable(Hashtable hash) {
refq = new ReferenceQueue();
this.hash = hash;
putAll(hash);
}
/**
* @param key
* @param value
*/
public Object put(Object key, Object value) {
checkQueue();
WeakRefValue ret = (WeakRefValue) super.put(key,
new WeakRefValue(value, refq, key));
return (ret == null ? null : ret.get());
}
/**
* @param key
* @return value
*/
public Object get(Object key) {
WeakRefValue ret = (WeakRefValue) super.get(key);
return (ret == null ? null : ret.get());
}
/**
* @param key
* @return removed object null if none
*/
public Object remove(Object key) {
WeakRefValue ret = (WeakRefValue) super.remove(key);
return (ret == null ? null : ret.get());
}
/** removes garbage collected values */
private void checkQueue() {
for (;;) {
WeakRefValue ref = (WeakRefValue) refq.poll();
if (ref == null) break;
remove(ref.getKey());
}
}
}
/** weak ref to value */
static class WeakRefValue extends WeakReference {
/** its key */
private Object key;
/**
* @param referenced
*/
public WeakRefValue(Object referenced, ReferenceQueue q, Object key) {
super (referenced, q);
this.key = key;
}
/** @return key for this value */
public Object getKey() {
return key;
}
}
public static char[] readContents(Reader r) throws IOException {
int read = 0;
int total = 0;
int offset;
char[] buffer;
List buflist = new LinkedList();
do {
buffer = new char[2048];
offset = 0;
while (offset < buffer.length) {
read = r.read(buffer, offset, buffer.length - offset);
if (read == -1) break;
offset += read;
}
if (offset > 0) buflist.add(buffer);
total += offset;
} while (read >= 0);
r.close();
buffer = new char[total];
Iterator it = buflist.iterator();
int offset2 = 0;
while (it.hasNext()) {
char[] buf = (char[])it.next();
int size = (it.hasNext()) ? buf.length : offset;
System.arraycopy(buf, 0, buffer, offset2, size);
offset2 += size;
}
return buffer;
}
/** Returns contents of fileobject fo. If the object is opened in the editor,
the function returns current contents of the edited document. In that case,
if <b>save</b> is true, the editor content is saved.
If the file is not opened in a JavaEditor, it is read from the disk and
guarded sections are filtered out.
@return contents of the file/editor document; guarded section markers are filtered out.
*/
public static char[] getContent(FileObject fo, boolean save, boolean filter, String encoding) throws IOException {
DataObject obj = DataObject.find(fo);
JavaEditor editor = null;
if (obj instanceof JavaDataObject)
editor = ((JavaDataObject) obj).getJavaEditor();
if ((editor != null) && (editor.isDocumentLoaded())) {
// loading from the memory (Document)
final javax.swing.text.Document doc = editor.getDocument();
final String[] str = new String[1];
// safely take the text from the document
Runnable run = new Runnable() {
public void run() {
try {
str[0] = doc.getText(0, doc.getLength());
}
catch (javax.swing.text.BadLocationException e) {
// impossible
}
}
};
if (save) {
SaveCookie cookie = (SaveCookie) obj.getCookie(SaveCookie.class);
if (cookie != null) {
cookie.save();
}
}
doc.render(run);
return str[0].toCharArray();
} else {
// loading from the file
InputStream is = new BufferedInputStream(fo.getInputStream());
Reader reader;
if (filter) {
reader = new JavaEditor.GuardedReader(is, true);
} else {
if (encoding == null) {
reader = new InputStreamReader(is);
} else {
reader = new InputStreamReader(is, encoding);
}
}
return readContents(reader);
}
}
/** Creates new input stream from the file object.
* Finds the java data object, checks if the document is loaded and
* and create the stream either from the file object either from the document.
* @param fo fileobject with the source
* @param store if there is required the building and storing the elements
* hierarchy
* @exception IOException if any i/o problem occured during reading
@deprecated ParserInputStream that is being created by this function does not
fit well in I18N environments.
*/
public static InputStream createInputStream(FileObject fo, boolean save, boolean store) throws IOException {
DataObject obj = DataObject.find(fo);
JavaEditor editor = null;
if (obj instanceof JavaDataObject)
editor = ((JavaDataObject) obj).getJavaEditor();
if ((editor != null) && (editor.isDocumentLoaded())) {
// loading from the memory (Document)
final javax.swing.text.Document doc = editor.getDocument();
final String[] str = new String[1];
// safely take the text from the document
Runnable run = new Runnable() {
public void run() {
try {
str[0] = doc.getText(0, doc.getLength());
}
catch (javax.swing.text.BadLocationException e) {
// impossible
}
}
};
if (save) {
SaveCookie cookie = (SaveCookie) obj.getCookie(SaveCookie.class);
if (cookie != null) {
cookie.save();
}
}
doc.render(run);
return new ParserInputStream(str[0]);
}
else {
// loading from the file
InputStream is = new BufferedInputStream(fo.getInputStream());
if (store) {
//PENDING - improve performance
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
OutputStreamWriter outWriter = new OutputStreamWriter(byteStream);
JavaEditor.GuardedReader reader = new JavaEditor.GuardedReader(is, true);
try {
int c;
while ((c = reader.read()) != -1) {
outWriter.write(c);
}
}
finally {
outWriter.close();
is.close();
}
is = new ByteArrayInputStream(byteStream.toByteArray());
}
return new ParserInputStream(is);
}
}
// ==================== The parser input stream ==========================
/** The input stream which holds all data which are read in the StringBuffer.
@deprecated The class doesn't process character data in the stream and
is not very usable in I18N environments.
*/
static class ParserInputStream extends InputStream {
/** The underlaying stream. */
private InputStream stream;
/** Whole text */
private String text;
/** The string buffer which collect the data. */
private StringBuffer buffer;
/** This flag determines if there is used the text field or buffer field.
* The constructor set it
*/
private boolean mode;
/** The counter of read chars */
private int counter;
/** Offset of the begins of the lines (e.g. offset of [line,col] is lines[line] + col - 1
*/
private int[] lines = new int[200];
/** Current line counter - it is used for filling the lines array in the read method
*/
int lineCounter = 2;
/** Length of the current line
*/
int currentLineLength = 0;
/** Creates the stream from the text. */
ParserInputStream(String text) {
stream = new StringBufferInputStream(text);
this.text = text;
mode = false;
counter = 0;
}
/** Creates the stream from the another stream. */
ParserInputStream(InputStream stream) {
this.stream = stream;
buffer = new StringBuffer();
mode = true;
}
/** Gets the part of the text which was already read.
* @param begin the begin index
* @param end the end index
*/
public String getString(int begin, int end) {
return mode ? buffer.substring(begin, end) : text.substring(begin, end);
}
/** Gets the part of the text which was already read.
* End is last position which was already read.
* @param begin the begin index
*/
public String getString(int begin) {
if (mode) {
return buffer.substring(begin);
}
else {
int end = Math.min(counter - 1, text.length());
return text.substring(begin, end);
}
}
/** Read one character from the stream. */
public int read() throws IOException {
int x = stream.read();
if (mode && (x != -1)) {
buffer.append((char)x);
counter++;
}
// counting line's length
if (x == (int)'\n') {
if (lineCounter == lines.length - 1) {
int[] newLines = new int[lineCounter + lineCounter];
System.arraycopy(lines, 0, newLines, 0, lines.length);
lines = newLines;
}
lines[lineCounter] = lines[lineCounter - 1] + currentLineLength + 1;
lineCounter++;
currentLineLength = 0;
}
else {
currentLineLength++;
}
return x;
}
/** Closes the stream */
public void close() throws IOException {
stream.close();
}
/** Compute offset in the stream from line and column.
* @return the offset
*/
int getOffset(int line, int column) {
return lines[line] + column - 1;
}
}
}
/*
* Log
* 24 Gandalf-post-FCS1.22.1.0 3/9/00 Svatopluk Dedic Support for FileObject
* content extraction into char[]
* 23 src-jtulach1.22 1/26/00 Ales Novak
* 22 src-jtulach1.21 1/12/00 Petr Hamernik i18n: perl script used (
* //NOI18N comments added )
* 21 src-jtulach1.20 12/22/99 Petr Hamernik Update V8 Parser - old
* one is still used..
* 20 src-jtulach1.19 12/8/99 Ales Novak
* 19 src-jtulach1.18 12/8/99 Ales Novak createInputStream -
* boolean added
* 18 src-jtulach1.17 12/6/99 Ales Novak
* 17 src-jtulach1.16 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 16 src-jtulach1.15 9/24/99 Petr Hamernik debug message removed
* 15 src-jtulach1.14 9/13/99 Petr Hamernik minor changes
* 14 src-jtulach1.13 8/12/99 Ales Novak class files could be
* 'perfectly' examined about their source file
* 13 src-jtulach1.12 8/9/99 Ian Formanek Generated Serial Version
* UID
* 12 src-jtulach1.11 6/9/99 Ian Formanek ---- Package Change To
* org.openide ----
* 11 src-jtulach1.10 5/10/99 Petr Hamernik
* 10 src-jtulach1.9 4/30/99 Petr Hamernik
* 9 src-jtulach1.8 4/22/99 Petr Hamernik indentationEngine finder
* 8 src-jtulach1.7 4/1/99 Petr Hamernik
* 7 src-jtulach1.6 3/29/99 Petr Hamernik
* 6 src-jtulach1.5 3/29/99 Petr Hamernik
* 5 src-jtulach1.4 3/29/99 Ian Formanek Commented out
* compareClassesNames method to make the code compilable
* 4 src-jtulach1.3 3/28/99 Ales Novak
* 3 src-jtulach1.2 3/28/99 Petr Hamernik
* 2 src-jtulach1.1 3/12/99 Petr Hamernik
* 1 src-jtulach1.0 3/10/99 Petr Hamernik
* $
*/